package avl.sv.client;

import avl.sv.client.tools.SelectTool;
import avl.sv.client.source.SolutionSourcePort;
import avl.sv.shared.Annotations.Annotation;
import avl.sv.shared.Annotations.AnnotationSet;
import avl.sv.shared.Annotations.ROI;
import avl.sv.shared.ClassifierClass;
import avl.sv.shared.ImageID;
import avl.sv.shared.JXTreeTableROI_Model;
import avl.sv.shared.Sample;
import avl.sv.shared.SampleSetClass;
import avl.sv.shared.SampleSetSlide;
import avl.sv.shared.Shapes;
import avl.sv.shared.SlideReference;
import avl.sv.shared.Solution;
import avl.sv.shared.TreeTransferHandler;
import avl.sv.shared.model.classifier.AbstractClassifier;
import avl.sv.shared.model.featureGenerator.AbstractFeatureGenerator;
import avl.sv.shared.sources.SlideSource;
import avl.sv.shared.sources.SolutionSource;
import ij.gui.MessageDialog;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.XMLDecoder;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.DropMode;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.ListSelectionModel;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;

/**
 *
 * @author benbryan
 */
public class SolutionManager extends JFrame implements MouseMotionListener, MouseListener {

    int cornerMarkerDim = 10;
    SolutionSource solutionSource;
    JXTreeTableROI_Model treeModelROI;
    Solution solution = new Solution("Classifier Set");
    MouseActionLogger mouseAction = new MouseActionLogger();
    private int overlayIdx = 0;


    public void setSolutionSource(SolutionSource ccss) {
        if (ccss == null) {
            jXTreeTableSolution.setTreeTableModel(new DefaultTreeTableModel());
            jButtonAddClass.setEnabled(false);
            jButtonGenerateModel.setEnabled(false);
            jButtonViewFeatures.setEnabled(false);
            jButtonClassify.setEnabled(false);
            jButtonSaveSolution.setEnabled(false);
            jButtonSetupModel.setEnabled(false);
            jTextFieldSolutionName.setText("");
            return;
        }
        jButtonAddClass.setEnabled(true);
        jButtonGenerateModel.setEnabled(true);
        jButtonSaveSolution.setEnabled(true);
        jButtonSetupModel.setEnabled(true);

        this.solutionSource = ccss;
        solution = ccss.getSolution();
        if (solution == null){
            return;
        }
        jTextFieldSolutionName.setText(ccss.getName());
        treeModelROI = new JXTreeTableROI_Model(solution);
        treeModelROI.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
                if (sv != null) {
                    sv.repaint();
                }
            }
        });

        jXTreeTableSolution.setTreeTableModel(treeModelROI);
        jButtonClassify.setEnabled(solution.hasValidClassifier());
        // Setting the treeTableModel above does not setup the colums properly
        // so this performes that function manually 
        TableColumnModel columnModel = new DefaultTableColumnModel();
        for (int i = 0; i < treeModelROI.getColumnCount(); i++) {
            TableColumn tc = new TableColumn();
            tc.setHeaderValue(treeModelROI.getColumnName(i));
            tc.setModelIndex(i);
            if (i > 0) {
                tc.setMaxWidth(50);
            }
            columnModel.addColumn(tc);
        }
        jXTreeTableSolution.setColumnModel(columnModel);
        jSpinnerTileSize.setValue(solution.tileDim);
        jSpinnerTileSize.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                int v = (int) jSpinnerTileSize.getValue();
                solution.tileDim = v;
                AdvancedVirtualMicroscope.getSelectedSlideviewer().repaint();
            }
        });
        updateGenerateModelButton();
    }

    public void paintROIs(SlideViewer sv, Graphics gOrig) {
        if (!isVisible()) {
            return;
        }
        if (solution == null) {
            return;
        }
        Graphics2D g = (Graphics2D) gOrig.create();
        sv.concatenateImageToDisplayTransform(g);
        paintTiles(sv, g);
        int nPts = 3;
        int dim = (int) (cornerMarkerDim / sv.getMagnification());
        for (ClassifierClass classifierClass : solution.getClasses()) {
            Path2D.Double marker = Shapes.getPolygon(nPts++, dim / 2);
            for (AnnotationSet annoSet : classifierClass.getAnnotationSets()) {
                if (ImageID.hashesAreEqual(sv.getSlideSource().getHash(), annoSet.getHash())) {
                    for (Annotation anno : annoSet.getAnnotations()) {
                        if (!anno.visible) {
                            continue;
                        }
                        for (ROI roi : anno.getRois()) {
                            g.setColor(new Color(anno.lineColor));
                            g.draw(roi.getShape());
                            if (roi.highlighted && jCheckBoxRegionHighlight.isSelected()) {
                                g.setColor(Color.blue);
                                Color c = g.getColor();
                                g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), 30));
                                g.fill(roi.getShape());
                            }
                            if (jCheckBoxShowSelectedMarkers.isSelected() || jCheckBoxShowAllMarkers.isSelected()) {
                                if (roi.selected && jCheckBoxShowSelectedMarkers.isSelected()) {
                                    g.setColor(Color.BLUE);
                                } else if (jCheckBoxShowAllMarkers.isSelected()) {
                                    g.setColor(Color.BLACK);
                                } else {
                                    continue;
                                }

                                // Draw markers
                                Polygon poly = roi.getPolygon();
                                if (poly != null) {
                                    for (int j = 0; j < poly.npoints; j++) {
                                        Path2D.Double tempMarker = (Path2D.Double) marker.clone();
                                        AffineTransform at = new AffineTransform();
                                        at.translate(poly.xpoints[j], poly.ypoints[j]);
                                        tempMarker.transform(at);
                                        g.draw(tempMarker);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void init() {
        jXTreeTableSolution.setDragEnabled(true);
        jXTreeTableSolution.setDropMode(DropMode.ON);
        jXTreeTableSolution.setTransferHandler(TreeTransferHandler.getInstance());
        jXTreeTableSolution.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getComponent() instanceof SlideViewer) {
            mouseAction.mousePressed(e);
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (e.getSource() instanceof SlideViewer) {
            mouseAction.mouseDragged(e);
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        Object src = e.getSource();
        if (src instanceof SlideViewer) {
            SlideViewer slideViewer = (SlideViewer) src;
            for (ClassifierClass classifierClass : solution.getClasses()) {
                for (AnnotationSet annoSet : classifierClass.getAnnotationSets()) {
                    if (ImageID.hashesAreEqual(annoSet.getHash(), slideViewer.getSlideSource().getHash())) {
                        highlightRegion(e.getPoint(), annoSet);
                    }
                }
            }
            slideViewer.repaint();
        }
    }

    private void highlightRegion(Point p, AnnotationSet annoSet) {
        if (annoSet == null) {
            return;
        }
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        Point p2 = (Point) p.clone();
        Graphics2D g = (Graphics2D) sv.getGraphics().create();
        sv.concatenateImageToDisplayTransform(g);
        try {
            g.getTransform().inverseTransform(p, p2);
        } catch (NoninvertibleTransformException ex) {
            Logger.getLogger(SolutionManager.class.getName()).log(Level.SEVERE, null, ex);
        }

        for (Annotation anno : annoSet.getAnnotations()) {
            for (ROI roi : anno.getRois()) {
                roi.highlighted = roi.containsPoint(p2);
            }
        }
    }

    public void updateButtons(final SlideViewer slideViewer, final Point point) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    if (point != null) {
                        setJXTreeTableSelectionPaths(point, slideViewer);
                    }
                    if (solution.getFeatureGenerators().isEmpty()) {
                        return;
                    }
                    if (solution.hasValidClassifier()) {
                        // Activate classify button if a roi is selectd in current slide viewer
                        AnnotationSet annoSet = slideViewer.getRoiManager().getSelectedROIs();
                        if (annoSet != null) {
                            jButtonClassify.setEnabled(true);
                            jButtonViewFeatures.setEnabled(true);
                            return;
                        }
                        // check for rois selected in this solution manager
                        ArrayList<ROI> rois = getSelectedROIsForSlideReference(slideViewer.getSlideSource().getSlideReference());
                        if (!rois.isEmpty()) {
                            jButtonClassify.setEnabled(true);
                            jButtonViewFeatures.setEnabled(true);
                        }
                    } else {
                        // Activate ViewFeatures button if a roi is selectd in current slide viewer
                        AnnotationSet annoSet = slideViewer.getRoiManager().getSelectedROIs();
                        if (annoSet != null) {
                            jButtonViewFeatures.setEnabled(true);
                            return;
                        }

                        // check for rois selected in this solution manager
                        ArrayList<ROI> rois = getSelectedROIsForSlideReference(slideViewer.getSlideSource().getSlideReference());
                        if (!rois.isEmpty()) {
                            jButtonViewFeatures.setEnabled(true);
                        }
                    }
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "", ex);
                }
            }

        });

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        jButtonClassify.setEnabled(false);
        jButtonViewFeatures.setEnabled(false);
        Object obj = e.getSource();
        final SlideViewer slideViewer = (SlideViewer) obj;
        if (obj instanceof SlideViewer) {
            if (slideViewer.getToolPanel().getSelectedTool() instanceof SelectTool) {
                final Point point = e.getPoint();
                updateButtons(slideViewer, point);
            }
        }
    }

    private void setJXTreeTableSelectionPaths(Point p, SlideViewer slideViewer) {
        // highlight selected rois in solution manager
        jXTreeTableSolution.getSelectionModel().clearSelection();
        Point pImg = slideViewer.displaySpaceToImageSpace(p);

        for (ClassifierClass classifierClass : solution.getClasses()) {
            for (AnnotationSet annoSet : classifierClass.getAnnotationSets()) {
                if (ImageID.hashesAreEqual(annoSet.getHash(), slideViewer.getSlideSource().getHash())) {
                    for (Annotation anno : annoSet.getAnnotations()) {
                        for (ROI roi : anno.getRois()) {
                            if (roi.containsPoint(pImg)) {
                                Object o[] = new Object[5];
                                o[0] = solution;
                                o[1] = classifierClass;
                                o[2] = annoSet;
                                o[3] = anno;
                                o[4] = roi;
                                TreePath path = new TreePath(o);
                                jXTreeTableSolution.expandPath(path.getParentPath());
                                int r = jXTreeTableSolution.getRowForPath(path);
                                jXTreeTableSolution.getSelectionModel().addSelectionInterval(r, r);
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        mouseAction.mouseReleased(e);
    }

    private void setupLookAndFeel() {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
        }
    }

    private ArrayList<SlideSource> getRequiredSlideSources(Solution solution) {
        ArrayList<SlideSource> slideSources = new ArrayList<>();
        for (ClassifierClass c : solution.getClasses()) {
            for (AnnotationSet annoSet : c.getAnnotationSets()) {
                boolean alreadyIncluded = false;
                for (SlideSource slideSource : slideSources) {
                    if (ImageID.hashesAreEqual(slideSource.getHash(), annoSet.getHash())) {
                        // If set already has this slide;
                        alreadyIncluded = true;
                        break;
                    }
                }
                if (!alreadyIncluded) {
                    SlideSource ss = AdvancedVirtualMicroscope.getSlideSource(annoSet.getSlideReference());
                    slideSources.add(ss);
                }
            }
        }
        return slideSources;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    private TreePath[] jXTreeTableSolutionGetSelectionPaths() {
        int[] rows = jXTreeTableSolution.getSelectedRows();
        TreePath paths[] = new TreePath[rows.length];
        for (int rIdx = 0; rIdx < rows.length; rIdx++) {
            paths[rIdx] = jXTreeTableSolution.getPathForRow(rows[rIdx]);
        }
        return paths;
    }

    private TreePath jXTreeTableSolutionGetLastSelectionPath() {
        int row = jXTreeTableSolution.getSelectedRow();
        return jXTreeTableSolution.getPathForRow(row);
    }

    private void paintTiles(SlideViewer slideViewer, Graphics gOrig) {
        if ((solution != null) && jCheckBoxShowTiles.isSelected()) {
            int tileDim = solution.tileDim;
            SlideSource slideSource = slideViewer.getSlideSource();
            for (int x = 0; x < slideSource.getImageDimX(); x += tileDim) {
                gOrig.drawLine(x, 0, x, slideSource.getImageDimY());
            }
            for (int y = 0; y < slideSource.getImageDimY(); y += tileDim) {
                gOrig.drawLine(0, y, slideSource.getImageDimX(), y);
            }
        }
    }

    private class SetButtonText implements Runnable {

        final private String str;
        final private JButton button;

        public SetButtonText(String str, JButton button) {
            this.str = str;
            this.button = button;
        }

        @Override
        public void run() {
            button.setText(str);
        }
    }

    private SolutionManager getThis() {
        return this;
    }

    private ArrayList<ROI> getSelectedROIsForSlideReference(SlideReference ref) {
        ArrayList<ROI> out = new ArrayList<>();
        // Returns ROIS within this solution with the same annoSet hash
        for (ClassifierClass classifierClass : solution.getClasses()) {
            for (AnnotationSet annoSet : classifierClass.getAnnotationSets()) {
                if (ImageID.hashesAreEqual(annoSet.getHash(), ref.hash)) {
                    for (Annotation anno : annoSet.getAnnotations()) {
                        for (ROI roi : anno.getRois()) {
                            if (roi.selected) {
                                out.add(roi);
                            }
                        }
                    }
                }
            }
        }
        return out;
    }

    private AnnotationSet getROIsSelectedInActiveSlideViewer() {
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        if (sv == null) {
            return null;
        }
        SlideReference sr = sv.getSlideSource().getSlideReference();

        AnnotationSet annoSet = sv.getRoiManager().getSelectedROIs();
        if (annoSet == null) {
            annoSet = new AnnotationSet(sr);
        }
        Annotation anno = Annotation.createDefault();
        annoSet.add(anno);
        // add in those rois selected in this solution manager
        for (ROI roi : getSelectedROIsForSlideReference(sr)) {
            anno.add((ROI) roi.clone());
        }

        return annoSet;
    }

    public SolutionManager() {
        setupLookAndFeel();
        initComponents();
        init();

        jXTreeTableSolution.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    final TreePath path = jXTreeTableSolution.getPathForLocation(e.getX(), e.getY());
                    if (path == null) {
                        return;
                    }
                    JPopupMenu menu = new JPopupMenu();
                    final Object node = path.getLastPathComponent();
                    if (node instanceof ROI) {
                        JMenuItem gotoROI = new JMenuItem("goto");
                        gotoROI.addActionListener(new AbstractAction() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                ROI roi = (ROI) node;
                                AnnotationSet annotationSet = (AnnotationSet) roi.getParent().getParent();
                                slideViewerToFront(annotationSet.getSlideReference());
                                AdvancedVirtualMicroscope.getSelectedSlideviewer().setImageRegion(roi.getShape().getBounds());
                            }
                        });
                        menu.add(gotoROI);
                    }
//                    if (node instanceof Annotation) {
//                        JMenuItem visible = new JMenuItem("Visible");
//                        visible.addActionListener(new AbstractAction() {
//                            @Override
//                            public void actionPerformed(ActionEvent e) {
//                                ((Annotation) node).visible = jCheckBoxMenuItemROIVisible.isSelected();
//                                AdvancedVirtualMicroscope.getSelectedSlideviewer().repaint();
//                            }
//                        });
//                        menu.add(visible);
//                    }

                    JMenuItem delete = new JMenuItem("Delete");
                    delete.addActionListener(new AbstractAction() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (node instanceof DefaultMutableTreeTableNode) {
                                treeModelROI.removeNodeFromParent((DefaultMutableTreeTableNode) node);
                            }
                            SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
                            if (sv != null) {
                                sv.repaint();
                            }
                        }
                    });
                    menu.add(delete);

                    menu.show(jXTreeTableSolution, e.getX(), e.getY());
                }
            }
        });
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jDialogNewSolution = new javax.swing.JDialog();
        jTextFieldNewSolutionName = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jTextAreaNewSolutionDescription = new javax.swing.JTextArea();
        jLabel3 = new javax.swing.JLabel();
        jButtonCreateNewSolution = new javax.swing.JButton();
        jButtonCancelCreateNewSolution = new javax.swing.JButton();
        jFileChooser1 = new javax.swing.JFileChooser();
        jPanel1 = new javax.swing.JPanel();
        jPanelAddRoi = new javax.swing.JPanel();
        jButtonSaveSolution = new javax.swing.JButton();
        jButtonNewSolution = new javax.swing.JButton();
        jButtonGenerateModel = new javax.swing.JButton();
        jCheckBoxShowSelectedMarkers = new javax.swing.JCheckBox();
        jCheckBoxRegionHighlight = new javax.swing.JCheckBox();
        jButtonClassify = new javax.swing.JButton();
        jCheckBoxShowAllMarkers = new javax.swing.JCheckBox();
        jCheckBoxRunOnServer = new javax.swing.JCheckBox();
        jButtonAddClass = new javax.swing.JButton();
        jButtonViewFeatures = new javax.swing.JButton();
        jSpinnerTileSize = new javax.swing.JSpinner();
        jLabel4 = new javax.swing.JLabel();
        jCheckBoxShowTiles = new javax.swing.JCheckBox();
        jButtonSetupModel = new javax.swing.JButton();
        jScrollPane3 = new javax.swing.JScrollPane();
        jXTreeTableSolution = new avl.sv.shared.JXTreeTableROIs();
        jLabel1 = new javax.swing.JLabel();
        jTextFieldSolutionName = new javax.swing.JTextField();

        jDialogNewSolution.setTitle("Create a new solution");
        jDialogNewSolution.setLocationByPlatform(true);

        jTextFieldNewSolutionName.setText("default");
        jTextFieldNewSolutionName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextFieldNewSolutionNameActionPerformed(evt);
            }
        });

        jLabel2.setText("Name");

        jTextAreaNewSolutionDescription.setColumns(20);
        jTextAreaNewSolutionDescription.setRows(5);
        jScrollPane2.setViewportView(jTextAreaNewSolutionDescription);

        jLabel3.setText("Description");

        jButtonCreateNewSolution.setText("Create");
        jButtonCreateNewSolution.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCreateNewSolutionActionPerformed(evt);
            }
        });

        jButtonCancelCreateNewSolution.setText("Cancel");
        jButtonCancelCreateNewSolution.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonCancelCreateNewSolutionActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jDialogNewSolutionLayout = new javax.swing.GroupLayout(jDialogNewSolution.getContentPane());
        jDialogNewSolution.getContentPane().setLayout(jDialogNewSolutionLayout);
        jDialogNewSolutionLayout.setHorizontalGroup(
            jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                        .addGap(0, 197, Short.MAX_VALUE)
                        .addGroup(jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                                .addComponent(jTextFieldNewSolutionName, javax.swing.GroupLayout.PREFERRED_SIZE, 149, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel2))
                            .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                                .addComponent(jButtonCreateNewSolution, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jButtonCancelCreateNewSolution, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE))))
                    .addComponent(jScrollPane2)
                    .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                        .addComponent(jLabel3)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        jDialogNewSolutionLayout.setVerticalGroup(
            jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jDialogNewSolutionLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jTextFieldNewSolutionName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jDialogNewSolutionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jButtonCreateNewSolution)
                    .addComponent(jButtonCancelCreateNewSolution))
                .addGap(26, 26, 26)
                .addComponent(jLabel3)
                .addGap(4, 4, 4)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        setTitle("Solution Manager");
        setLocationByPlatform(true);

        jPanel1.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 2, true));

        jButtonSaveSolution.setText("Save Solution");
        jButtonSaveSolution.setActionCommand("Save Classifier");
        jButtonSaveSolution.setEnabled(false);
        jButtonSaveSolution.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSaveSolutionActionPerformed(evt);
            }
        });

        jButtonNewSolution.setText("New Solution");
        jButtonNewSolution.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonNewSolutionActionPerformed(evt);
            }
        });

        jButtonGenerateModel.setText("Generate Model");
        jButtonGenerateModel.setEnabled(false);
        jButtonGenerateModel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonGenerateModelActionPerformed(evt);
            }
        });

        jCheckBoxShowSelectedMarkers.setSelected(true);
        jCheckBoxShowSelectedMarkers.setText("Show Selected Markers");
        jCheckBoxShowSelectedMarkers.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxShowSelectedMarkersActionPerformed(evt);
            }
        });

        jCheckBoxRegionHighlight.setSelected(true);
        jCheckBoxRegionHighlight.setText("Region Highlight");
        jCheckBoxRegionHighlight.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxRegionHighlightActionPerformed(evt);
            }
        });

        jButtonClassify.setText("Classify");
        jButtonClassify.setToolTipText("");
        jButtonClassify.setEnabled(false);
        jButtonClassify.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonClassifyActionPerformed(evt);
            }
        });

        jCheckBoxShowAllMarkers.setText("Show All Markers");
        jCheckBoxShowAllMarkers.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxShowAllMarkersActionPerformed(evt);
            }
        });

        jCheckBoxRunOnServer.setText("Run On Server");

        jButtonAddClass.setText("Add Class");
        jButtonAddClass.setEnabled(false);
        jButtonAddClass.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonAddClassActionPerformed(evt);
            }
        });

        jButtonViewFeatures.setText("View Features");
        jButtonViewFeatures.setEnabled(false);
        jButtonViewFeatures.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonViewFeaturesActionPerformed(evt);
            }
        });

        jSpinnerTileSize.setModel(new javax.swing.SpinnerNumberModel(128, 1, 512, 1));

        jLabel4.setText("Tile Size");

        jCheckBoxShowTiles.setText("Show Tiles");
        jCheckBoxShowTiles.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckBoxShowTilesActionPerformed(evt);
            }
        });

        jButtonSetupModel.setText("Setup Model");
        jButtonSetupModel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonSetupModelActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanelAddRoiLayout = new javax.swing.GroupLayout(jPanelAddRoi);
        jPanelAddRoi.setLayout(jPanelAddRoiLayout);
        jPanelAddRoiLayout.setHorizontalGroup(
            jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jCheckBoxShowTiles)
                    .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                        .addComponent(jButtonViewFeatures, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE)
                        .addComponent(jButtonNewSolution, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonSaveSolution, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonAddClass, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addComponent(jButtonClassify, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jCheckBoxRegionHighlight)
                    .addComponent(jCheckBoxShowSelectedMarkers)
                    .addComponent(jCheckBoxShowAllMarkers)
                    .addComponent(jCheckBoxRunOnServer)
                    .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                        .addComponent(jSpinnerTileSize, javax.swing.GroupLayout.PREFERRED_SIZE, 72, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel4))
                    .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                        .addComponent(jButtonSetupModel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jButtonGenerateModel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE)))
                .addContainerGap(12, Short.MAX_VALUE))
        );
        jPanelAddRoiLayout.setVerticalGroup(
            jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanelAddRoiLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jButtonNewSolution)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonSaveSolution)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonAddClass)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonViewFeatures)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonSetupModel)
                .addGap(7, 7, 7)
                .addComponent(jButtonGenerateModel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonClassify)
                .addGap(3, 3, 3)
                .addGroup(jPanelAddRoiLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jSpinnerTileSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel4))
                .addGap(18, 18, 18)
                .addComponent(jCheckBoxRegionHighlight)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxShowSelectedMarkers)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxShowTiles)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxShowAllMarkers)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBoxRunOnServer)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jXTreeTableSolution.setDropMode(javax.swing.DropMode.ON);
        jXTreeTableSolution.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jXTreeTableSolutionMouseClicked(evt);
            }
        });
        jXTreeTableSolution.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                jXTreeTableSolutionValueChanged(evt);
            }
        });
        jScrollPane3.setViewportView(jXTreeTableSolution);

        jLabel1.setText("Solution Name");

        jTextFieldSolutionName.setText("n/a");
        jTextFieldSolutionName.setEnabled(false);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jTextFieldSolutionName, javax.swing.GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE))
                    .addComponent(jScrollPane3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanelAddRoi, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanelAddRoi, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(5, 5, 5)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jTextFieldSolutionName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 399, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 475, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 434, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jButtonSaveSolutionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSaveSolutionActionPerformed
        jButtonSaveSolution.setEnabled(false);
        String oldText = jButtonSaveSolution.getText();
        jButtonSaveSolution.setText("Sending...");
        try {
            String result = solutionSource.setSolution(solution);
            if ((result == null) || result.startsWith("error:")){
                AdvancedVirtualMicroscope.setStatusText(result, 4000);
                jButtonSaveSolution.setText("Failure");
            } else {
                jButtonSaveSolution.setText("Success");
            }
        } catch (Exception ex) {
            Logger.getLogger(SolutionManager.class.getName()).log(Level.SEVERE, null, ex);
            jButtonSaveSolution.setText("Failure");
        }
        jButtonSaveSolution.setEnabled(true);
        Executors.newSingleThreadScheduledExecutor().schedule(new SetButtonText(oldText, jButtonSaveSolution), 5, TimeUnit.SECONDS);
    }//GEN-LAST:event_jButtonSaveSolutionActionPerformed

    private void jButtonNewSolutionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonNewSolutionActionPerformed
        jDialogNewSolution.setVisible(true);
        jDialogNewSolution.setSize(jDialogNewSolution.getPreferredSize());
    }//GEN-LAST:event_jButtonNewSolutionActionPerformed

    private void updateGenerateModelButton() {
        if (solution.getClassifiers().isEmpty()) {
            jButtonGenerateModel.setEnabled(false);
            return;
        }
        int numelFeatures = 0;
        for (AbstractFeatureGenerator generator : solution.getFeatureGenerators()) {
            numelFeatures += generator.getNumberOfFeatures();
        }
        if (numelFeatures == 0) {
            jButtonGenerateModel.setEnabled(false);
            return;
        }
        jButtonGenerateModel.setEnabled(true);
    }

    private void jButtonGenerateModelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonGenerateModelActionPerformed

        // Setup Parameters
        jButtonClassify.setEnabled(false);
        jButtonGenerateModel.setEnabled(false);

        // Generate model
        if (jCheckBoxRunOnServer.isSelected()) {
            Executors.newSingleThreadExecutor().submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        ProgressMonitor monitorRunningOnServer = new ProgressMonitor(getThis(), "Training classifier on server", "Saving solution to server", 0, 100);

                        // Save to server first
                        solutionSource.setSolution(solution);
                        monitorRunningOnServer.setProgress(10);
                        monitorRunningOnServer.setNote("waiting for server");
                        // Train on server
                        ArrayList<AbstractClassifier> newClassifiers = null;
                        try {
                            byte[] b = AdvancedVirtualMicroscope.getTrainingPort().trainClassifier(solutionSource.getId());
                            if (!monitorRunningOnServer.isCanceled()) {
                                monitorRunningOnServer.setProgress(70);
                                monitorRunningOnServer.setNote("Parsing reply");
                                ByteArrayInputStream is = new ByteArrayInputStream(b);
                                XMLDecoder d = new XMLDecoder(is);
                                Object obj = d.readObject();
                                if (obj instanceof ArrayList) {
                                    newClassifiers = (ArrayList<AbstractClassifier>) obj;
                                    MessageDialog md = new MessageDialog(null, "Info", "Model updated from server");
                                } else {
                                    return;
                                }
                            }
                        } catch (Exception ex) {
                            MessageDialog md = new MessageDialog(null, "Error", "Failed to generate model on server");
                            updateGenerateModelButton();
                        } finally {
                            for (AbstractClassifier newClassifier : newClassifiers) {
                                if (newClassifier.isActive()) {
                                    solution.setClassifier(newClassifier);
                                }
                            }
                            for (AbstractClassifier classifier : solution.getClassifiers()) {
                                if (classifier.isValid()) {
                                    jButtonClassify.setEnabled(true);
                                }
                            }
                        }
                        jButtonGenerateModel.setEnabled(true);
                    } catch (Exception ex) {
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, "", ex);
                    }
                }
            });
        } else {
            // Run training locally
            jButtonGenerateModel.setEnabled(false);
            jButtonClassify.setEnabled(false);
            Executors.newSingleThreadExecutor().submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        ArrayList<SlideSource> slideSources = getRequiredSlideSources(solution);
                        ProgressMonitor pm = new ProgressMonitor(getThis(), "Generating Features", "", 0, SampleSetClass.numelSamples(solution, slideSources) + 5);
                        pm.setMillisToPopup(500);
                        ArrayList<SampleSetClass> samplesSets = SampleSetClass.generateTrainingSets(slideSources, solution, pm);
                        if (samplesSets == null) {
                            if (!pm.isCanceled()) {
                                MessageDialog md = new MessageDialog(null, "Error", "Failed to generate features");
                            }
                            jButtonGenerateModel.setEnabled(true);
                            jButtonClassify.setEnabled(true);
                        }

                        // Check if there are samples in each set
                        for (SampleSetClass sampleSet : samplesSets) {
                            if (sampleSet.samples.size() < 10) {
                                MessageDialog md = new MessageDialog(null, "Error", "Not enough samples collected");
                                jButtonGenerateModel.setEnabled(true);
                                jButtonClassify.setEnabled(true);
                                return;
                            }
                        }
                        pm.setNote("Training classifier(s)");
                        solution.trainClassifiers(samplesSets);
                        Iterator<AbstractClassifier> iter = solution.getClassifierIterator();
                        while (iter.hasNext()) {
                            AbstractClassifier classifier = iter.next();
                            if (classifier.isActive()) {
                                MessageDialog md = new MessageDialog(null, "Classifier Results", classifier.getMessage());
                            }
                        }

                        jButtonGenerateModel.setEnabled(true);
                        jButtonClassify.setEnabled(true);
                        pm.close();
                    } catch (Exception ex) {
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, "", ex);
                    }
                }
            });
        }
    }//GEN-LAST:event_jButtonGenerateModelActionPerformed

    private void jCheckBoxShowSelectedMarkersActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxShowSelectedMarkersActionPerformed
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        if (sv != null) {
            sv.repaint();
        }
    }//GEN-LAST:event_jCheckBoxShowSelectedMarkersActionPerformed

    private void jCheckBoxRegionHighlightActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxRegionHighlightActionPerformed
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        if (sv != null) {
            sv.repaint();
        }
    }//GEN-LAST:event_jCheckBoxRegionHighlightActionPerformed

    private void jButtonClassifyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonClassifyActionPerformed

        // Setup Parameters
        final AnnotationSet annotationSet = getROIsSelectedInActiveSlideViewer();
        final SlideViewer slideViewer = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        final SlideSource slideSource = slideViewer.getSlideSource();
        final Solution solutionHold = solution;
        if (annotationSet == null) {
            jButtonClassify.setEnabled(true);
            return;
        }
        jButtonClassify.setEnabled(false);

        Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                try {
                    ProgressMonitor pm = new ProgressMonitor(getThis(), "Preparing labels for display", "Locating samples", 0, 100);
                    pm.setMillisToPopup(500);
                    SampleSetSlide sampleSetSlide = new SampleSetSlide(slideSource, annotationSet, solutionHold);
                    pm.setMaximum((int) (sampleSetSlide.samples.size()*1.5));
                    sampleSetSlide.getSampleFeatures(0, solution, pm);
                    if (pm.isCanceled()) {
                        return;
                    }
                    ArrayList<Sample> samples = sampleSetSlide.samples;
                    if (samples == null) {
                        if (!pm.isCanceled()) {
                            MessageDialog md = new MessageDialog(null, "Error", "Failed to generate features");
                        }
                    } else {
                        Iterator<AbstractClassifier> iter = solutionHold.getClassifierIterator();
                        while (iter.hasNext()) {
                            AbstractClassifier classifier = iter.next();
                            classifier.predict(samples);
                            ClassifierResults cr = new ClassifierResults(classifier.getName(), slideViewer, samples, classifier.getLabels(), classifier.getClassNames(), "Overlay #" + String.valueOf(overlayIdx++));
                            slideViewer.addPlugin(cr);
                        }
                        jButtonClassify.setEnabled(true);
                        pm.close();
                    }
                    jButtonViewFeatures.setEnabled(true);
                    pm.close();
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }//GEN-LAST:event_jButtonClassifyActionPerformed

    private void jCheckBoxShowAllMarkersActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxShowAllMarkersActionPerformed
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        if (sv != null) {
            sv.repaint();
        }
    }//GEN-LAST:event_jCheckBoxShowAllMarkersActionPerformed

    private void jButtonAddClassActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonAddClassActionPerformed
        ClassifierClass c = new ClassifierClass("Default");
        solution.add(c);
        jXTreeTableSolution.updateUI();
        //        TreePath tp = new TreePath(new Object[]{annotations, anno});
        //        jXTreeTableSolution.setSelectionPath(tp);
    }//GEN-LAST:event_jButtonAddClassActionPerformed

    private void jButtonViewFeaturesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonViewFeaturesActionPerformed
        // Setup Parameters
        final AnnotationSet annotationSet = getROIsSelectedInActiveSlideViewer();
        final SlideViewer slideViewer = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        final SlideSource slideSource = slideViewer.getSlideSource();
        final Solution solutionHold = solution;
        if (annotationSet == null) {
            jButtonViewFeatures.setEnabled(true);
            return;
        }
        jButtonViewFeatures.setEnabled(false);
        Future<?> future = Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                try {
                    ProgressMonitor pm = new ProgressMonitor(getThis(), "Preparing features for display", "Locating samples", 1, 100);
                    pm.setMillisToPopup(500);
                    SampleSetSlide sampleSetSlide = new SampleSetSlide(slideSource, annotationSet, solutionHold);
                    pm.setMaximum(sampleSetSlide.samples.size());
                    sampleSetSlide.getSampleFeatures(0, solution, pm);
                    if (pm.isCanceled()) {
                        return;
                    }
                    if (sampleSetSlide.samples == null) {
                        if (!pm.isCanceled()) {
                            MessageDialog md = new MessageDialog(null, "Error", "Failed to generate features");
                        }
                    } else {
                        Iterator<AbstractFeatureGenerator> iter = solutionHold.getFeatureIterator();
                        ArrayList<String> featureNames = new ArrayList<>();
                        while (iter.hasNext()) {
                            String[] names = iter.next().getFeatureNames();
                            featureNames.addAll(Arrays.asList(names));
                        }
                        if (sampleSetSlide.samples.isEmpty()){
                            MessageDialog md = new MessageDialog(null, "Error", "No samples were collected");
                            pm.close();
                            return;
                        }
                        FeatureDisplay fd = new FeatureDisplay(slideViewer, sampleSetSlide.samples, featureNames.toArray(new String[featureNames.size()]), "Overlay #" + String.valueOf(overlayIdx++));
                        slideViewer.addPlugin(fd);
                    }
                    jButtonViewFeatures.setEnabled(true);
                    pm.close();
                } catch (Exception ex) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }//GEN-LAST:event_jButtonViewFeaturesActionPerformed
    
    private void slideViewerToFront(SlideReference slideReference) {
        ArrayList<SlideViewer> openSlides = AdvancedVirtualMicroscope.getOpenSlideViewers();

        // In case SlideViwer is already open
        for (SlideViewer sv : openSlides) {
            if (ImageID.hashesAreEqual(sv.getSlideSource().getHash(), slideReference.hash)) {
                AdvancedVirtualMicroscope.setTopSlideViewer(sv);
                AdvancedVirtualMicroscope.getInstance().toFront();
                return;
            }
        }
        // If not already open, open it
        SlideSource ss = AdvancedVirtualMicroscope.getSlideSource(slideReference);
        AdvancedVirtualMicroscope.addSlideViewer(ss);
        AdvancedVirtualMicroscope.getInstance().toFront();
    }

    private void jXTreeTableSolutionMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jXTreeTableSolutionMouseClicked
        if ((evt.getClickCount() == 2) && (evt.getButton() == MouseEvent.BUTTON1)) {
            Object obj = jXTreeTableSolution.getSelectionPath().getLastPathComponent();
            if (obj instanceof AnnotationSet) {
                // Bring Slideviewer for this set to front
                AnnotationSet annoSet = (AnnotationSet) obj;
                slideViewerToFront(annoSet.getSlideReference());
            }
        }
    }//GEN-LAST:event_jXTreeTableSolutionMouseClicked

    private void jXTreeTableSolutionValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_jXTreeTableSolutionValueChanged
        for (ClassifierClass classifierClass : solution.getClasses()) {
            for (AnnotationSet annoSet : classifierClass.getAnnotationSets()) {
                for (Annotation anno : annoSet.getAnnotations()) {
                    ArrayList<ROI> rois = anno.getRois();
                    for (ROI roi : rois) {
                        roi.selected = false;
                    }
                }
            }
        }

        TreePath[] tPath = jXTreeTableSolutionGetSelectionPaths();
        if (tPath == null) {
            jButtonViewFeatures.setEnabled(false);
            return;
        }
        for (TreePath path : tPath) {
            try {
                DefaultMutableTreeTableNode node = (DefaultMutableTreeTableNode) path.getLastPathComponent();
                if (node instanceof ClassifierClass) {
                    for (AnnotationSet annoSet : ((ClassifierClass) node).getAnnotationSets()) {
                        for (Annotation anno : annoSet.getAnnotations()) {
                            ArrayList<ROI> rois = anno.getRois();
                            for (ROI roi : rois) {
                                roi.selected = true;
                            }
                        }
                    }
                }

                if (node instanceof AnnotationSet) {
                    for (Annotation anno : ((AnnotationSet) node).getAnnotations()) {
                        ArrayList<ROI> rois = anno.getRois();
                        for (ROI roi : rois) {
                            roi.selected = true;
                        }
                    }
                }

                if (node instanceof Annotation) {
                    ArrayList<ROI> rois = ((Annotation) node).getRois();
                    for (ROI roi : rois) {
                        roi.selected = true;
                    }
                }

                if (node instanceof ROI) {
                    ((ROI) node).selected = true;
                }

            } catch (Exception ex) {
            }

        }
        SlideViewer sv = AdvancedVirtualMicroscope.getSelectedSlideviewer();
        if (sv != null) {
            sv.repaint();
        }
    }//GEN-LAST:event_jXTreeTableSolutionValueChanged

    private void jTextFieldNewSolutionNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextFieldNewSolutionNameActionPerformed

    }//GEN-LAST:event_jTextFieldNewSolutionNameActionPerformed

    private void jButtonCreateNewSolutionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCreateNewSolutionActionPerformed
        String name = jTextFieldNewSolutionName.getText();
        String desc = jTextAreaNewSolutionDescription.getText();
        String result = AdvancedVirtualMicroscope.getTrainingPort().createSolution(name, desc);
        int id = Integer.valueOf(result);
        if (result.startsWith("error:") || (id < 0)) {
            MessageDialog md = new MessageDialog(null, "Error", "Solution already exists");
            return;
        }

        jButtonSaveSolution.setEnabled(true);
        SolutionSource ss = new SolutionSourcePort(AdvancedVirtualMicroscope.getTrainingPort(), name, id);
        setSolutionSource(ss);
        jDialogNewSolution.setVisible(false);
        AdvancedVirtualMicroscope.updateSolutionsTree();
    }//GEN-LAST:event_jButtonCreateNewSolutionActionPerformed

    private void jButtonCancelCreateNewSolutionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCancelCreateNewSolutionActionPerformed
        jDialogNewSolution.setVisible(false);
    }//GEN-LAST:event_jButtonCancelCreateNewSolutionActionPerformed

    private void jCheckBoxShowTilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxShowTilesActionPerformed
        AdvancedVirtualMicroscope.getSelectedSlideviewer().repaint();
    }//GEN-LAST:event_jCheckBoxShowTilesActionPerformed

    private void jButtonSetupModelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonSetupModelActionPerformed
        ModelSetup ms = new ModelSetup(this, true, solution);
        ms.setVisible(true);
        updateGenerateModelButton();
    }//GEN-LAST:event_jButtonSetupModelActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButtonAddClass;
    private javax.swing.JButton jButtonCancelCreateNewSolution;
    private javax.swing.JButton jButtonClassify;
    private javax.swing.JButton jButtonCreateNewSolution;
    private javax.swing.JButton jButtonGenerateModel;
    private javax.swing.JButton jButtonNewSolution;
    private javax.swing.JButton jButtonSaveSolution;
    private javax.swing.JButton jButtonSetupModel;
    private javax.swing.JButton jButtonViewFeatures;
    private javax.swing.JCheckBox jCheckBoxRegionHighlight;
    private javax.swing.JCheckBox jCheckBoxRunOnServer;
    private javax.swing.JCheckBox jCheckBoxShowAllMarkers;
    private javax.swing.JCheckBox jCheckBoxShowSelectedMarkers;
    private javax.swing.JCheckBox jCheckBoxShowTiles;
    private javax.swing.JDialog jDialogNewSolution;
    private javax.swing.JFileChooser jFileChooser1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanelAddRoi;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JSpinner jSpinnerTileSize;
    private javax.swing.JTextArea jTextAreaNewSolutionDescription;
    private javax.swing.JTextField jTextFieldNewSolutionName;
    private javax.swing.JTextField jTextFieldSolutionName;
    private avl.sv.shared.JXTreeTableROIs jXTreeTableSolution;
    // End of variables declaration//GEN-END:variables
}
